﻿using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;

using LeanEngine.Entity;
using System.Configuration;

namespace LeanEngine.Data.Linq
{
    /// <summary>
    /// Represents the data provider which translates the expressions linq queries in T-SQL.
    /// </summary>
    public sealed class DatabaseContext : IDisposable
    {
        #region Declaration Members

        private bool _disposed;        
        private readonly IDatabaseContext _provider;
        private readonly Dictionary<MetaTable, IQueryableTable> _tables;

        #endregion

        #region Constructor Members

        /// <summary>
        /// Initialize a new instance of LeanEngine.Data.Linq.DatabaseContext class.
        /// </summary>
        public DatabaseContext()
        {
#if TRIAL_BUILD
            DateTime.Now.Evaluate();
#endif
            _tables = new Dictionary<MetaTable, IQueryableTable>();
            _provider = new QueryProvider(this);
        }

        #endregion

        #region PropertyInfo Members

        internal IDatabaseContext Provider
        {
            get
            {
                CheckDispose();
                return _provider;
            }
        }

        #endregion

        #region Method Members

        /// <summary>
        /// Gets the database table to execute queries against it.
        /// </summary>
        /// <typeparam name="TEntity">The System.Type of the target table.</typeparam>
        public QueryableEntity<TEntity> GetTable<TEntity>() 
            where TEntity : PlainEntity
        {
            CheckDispose();
            var entityType = typeof(TEntity);
            MetaTable metaTable = MetaTableCache.GetItem(entityType);

            var table = GetTable(entityType, metaTable);
            if (table.ElementType != entityType) throw new Mapping.MappingException("Was not possible to retrieve a queryable table for " + entityType.FullName);

            return (QueryableEntity<TEntity>)table;
        }

        private IQueryableTable GetTable(Type entityType, MetaTable metaTable)
        {
            IQueryableTable table;
            if (!_tables.TryGetValue(metaTable, out table))
            {
                table = (IQueryableTable)Activator.CreateInstance(
                  typeof(QueryableEntity<>).MakeGenericType(
                    new[] { entityType }),
                    BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null,
                    new object[] { this, metaTable }, null);

                _tables.Add(metaTable, table);
            }
            return table;
        }

        private void CheckDispose()
        {
            if (_disposed)
                throw new ObjectDisposedException(typeof(DatabaseContext).Name);
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or
        /// resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            _disposed = disposing;
            if (_provider != null) _provider.Dispose();
            if (_tables != null) _tables.Clear();
        }

        #endregion
    }
}